package com.kaka.jsonmap.core;

import com.kaka.jsonmap.core.evaluator.DictionaryManagerAware;
import com.kaka.jsonmap.core.evaluator.ExpressionEvaluator;
import com.kaka.jsonmap.core.mapper.DefaultFieldMapper;
import com.kaka.jsonmap.core.mapper.FieldMapper;
import com.kaka.jsonmap.model.InterfaceMapping;
import com.kaka.jsonmap.model.MappingConfig;
import com.jayway.jsonpath.internal.Utils;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

/**
 * MappingEngine is the core class for mapping fields based on a configuration.
 * It uses ServiceLoader to load MappingStrategies and ExpressionEvaluators,
 * and delegates field mapping to a FieldMapper.
 */
@Slf4j
public class MappingEngine implements FieldMapper {
    private final MappingConfig mappingConfig;
    private final FieldMapper fieldMapper;
    private final DictionaryManager dictionaryManager;

    /**
     * Constructs a MappingEngine with the given configuration.
     *
     * @param mappingConfig The mapping configuration
     */
    public MappingEngine(MappingConfig mappingConfig) {
        this.mappingConfig = mappingConfig;
        this.dictionaryManager = new DictionaryManager();
        if (mappingConfig.getDictMap() != null) {
            dictionaryManager.loadDictionaries(mappingConfig.getDictMap());
        }
        // Load all ExpressionEvaluators using ServiceLoader
        List<ExpressionEvaluator> evaluators = loadExpressionEvaluator();
        for (ExpressionEvaluator evaluator : evaluators) {
            if (evaluator instanceof DictionaryManagerAware) {
                ((DictionaryManagerAware) evaluator).setDictionaryManager(dictionaryManager);
            }
        }
        this.fieldMapper = new DefaultFieldMapper(new ValueTransformer(evaluators));
    }

    private List<ExpressionEvaluator> loadExpressionEvaluator() {
        List<ExpressionEvaluator> evaluators = new ArrayList<>();
        ServiceLoader<ExpressionEvaluator> evaluatorLoader = ServiceLoader.load(ExpressionEvaluator.class);
        for (ExpressionEvaluator evaluator : evaluatorLoader) {
            evaluators.add(evaluator);
        }
        return evaluators;
    }

    /**
     * Maps fields based on the provided mapping definitions and source object.
     *
     * @param mappingDefinitions The mapping definitions
     * @param sourceObject       The source object to map from
     * @return The mapped result
     */
    @Override
    public Map<String, Object> mapFields(Map<String, Object> mappingDefinitions, Map<String, Object> sourceObject) {
        Utils.notNull(mappingDefinitions, "mappingDefinitions cannot be null or empty");
        return fieldMapper.mapFields(mappingDefinitions, sourceObject);
    }

    /**
     * Maps a request body based on the specified command and source object.
     *
     * @param command      The command to match in the interface mappings
     * @param sourceObject The source object to map from
     * @return The mapped request body
     * @throws IllegalArgumentException if the command is not found
     */
    public Map<String, Object> mapRequestBodyByCommand(String command, Map<String, Object> sourceObject) {
        InterfaceMapping interfaceMapping = mappingConfig.getInterfaces().stream()
                .filter(x -> x.getCommand().equals(command))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Command not found: " + command));
        return this.mapFields(interfaceMapping.getRequestBody(), sourceObject);
    }
}